package com.atguigu.auth.service.impl;
import com.atguigu.auth.mapper.SysMenuMapper;
import com.atguigu.auth.service.SysMenuService;
import com.atguigu.auth.service.SysRoleMenuService;
import com.atguigu.auth.utils.MenuHelper;
import com.atguigu.model.system.SysRole;
import com.atguigu.model.system.SysRoleMenu;
import com.atguigu.vo.system.AssginMenuVo;
import com.atguigu.vo.system.MetaVo;
import com.atguigu.vo.system.RouterVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mysql.cj.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.atguigu.model.system.SysMenu;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 菜单表 服务实现类
 * </p>
 *
 * @author 党凯旋
 * @since 2024-08-25
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    //查询所有菜单和根据角色Id获取分配给角色的菜单  （菜单表中isSelect要变为true）
    @Override
    public List<SysMenu> findSysMenuByRoleId(Long roleId) {
        //1.查询所有菜单 status==1菜单才能用
        LambdaQueryWrapper<SysMenu> sysmenuWq = new LambdaQueryWrapper<>();
        sysmenuWq.eq(SysMenu::getStatus,1);
        List<SysMenu> sysMenuList = baseMapper.selectList(sysmenuWq);

        //2.根据角色ID查询角色对应的所有菜单ID (SysRoleMenu)
        LambdaQueryWrapper<SysRoleMenu> menuRoleWq = new LambdaQueryWrapper<>();
        menuRoleWq.eq(SysRoleMenu::getRoleId,roleId);
        List<SysRoleMenu> sysRoleMenuList = sysRoleMenuService.list(menuRoleWq);

        List<Long> menuIds = sysRoleMenuList.stream().map(item ->
                item.getMenuId()).collect(Collectors.toList());

        //3.拿菜单ID和所有菜单进行比较 有对应ID的  菜单表中的isSelect要变为true
        sysMenuList.stream().forEach( item -> {
            //查询出角色对应菜单存在于 设置为true
            if (menuIds.contains(item.getId())){
                item.setSelect(true);
            }else {
                item.setSelect(false);
            }
        });

        //4.返回规定格式的菜单属性结构
        List<SysMenu> sysMenus = MenuHelper.buildTree(sysMenuList);
        return sysMenus;
    }


    //给角色分配菜单
    @Override
    public void doAssign(AssginMenuVo assginMenuVo) {
        //1. 根据角色ID删除角色菜单表中的数据
        LambdaQueryWrapper<SysRoleMenu> sysRoleMenuWq = new LambdaQueryWrapper<>();
        sysRoleMenuWq.eq(SysRoleMenu::getRoleId,assginMenuVo.getRoleId());
        sysRoleMenuService.remove(sysRoleMenuWq);


        //2.从参数中获取角色新分配的菜单ID 进行遍历 然后将数据添加入角色菜单表中
        List<Long> menuIdList = assginMenuVo.getMenuIdList();
        List<SysRoleMenu> sysRoleMenuList = new ArrayList<>();

        for (Long menuId : menuIdList){
            if (StringUtils.isNullOrEmpty(menuId.toString())){
                continue;
            }
            SysRoleMenu sysRoleMenu = new SysRoleMenu();
            sysRoleMenu.setMenuId(menuId);
            sysRoleMenu.setRoleId(assginMenuVo.getRoleId());
            sysRoleMenuList.add(sysRoleMenu);
            //方法1 单个插入
            //sysRoleMenuService.save(sysRoleMenu);
        }
        //方法2 批量保存
        //将数据逐条存入数据中
        sysRoleMenuService.saveBatch(sysRoleMenuList);
    }

    @Override
    public List<SysMenu> findNodes() {

        //1.查询所有菜单的数据
        List<SysMenu> sysMenusList = baseMapper.selectList(null);

        //2.构建出属性结构
        /**
         * {
         *     第一层
         *     children:[
         *         第二层
         *          children:[
         *             第...
         *          ]
         *     ]
         * }
         */
        List<SysMenu>  resultList =MenuHelper.buildTree(sysMenusList);
        return resultList;
    }

    @Override
    public void removeMenuById(Long id) {
        //判断当前菜单是否有下一层菜单
        LambdaQueryWrapper<SysMenu> qw = new LambdaQueryWrapper<>();
        qw.eq(SysMenu::getParentId,id);
        Integer count = baseMapper.selectCount(qw);
        if (count>0){
            throw new RuntimeException("当前菜单有子菜单，无法删除");
        }
        baseMapper.deleteById(id);
    }


    //根据用户ID获取用户可以操作的菜单列表(前端的路由不能写死)
    @Override
    public List<RouterVo> findUserMenuListByUserId(Long userId) {
        //集合最终都要构建 放在外面统一构建
        List<SysMenu> sysMenuList = new ArrayList<>();

        //1. 判断当前用户是否为管理员 userID = 1
        //1.1 如果是管理员 查询出所有菜单数据
        if (userId.longValue()==1){
            //查询出所有菜单数据
            LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = new LambdaQueryWrapper<>();
            //状态为1 正在使用 为0禁用
            sysMenuLambdaQueryWrapper.eq(SysMenu::getStatus,1);
            //按照升序排序
            sysMenuLambdaQueryWrapper.orderByAsc(SysMenu::getSortValue);
            sysMenuList = baseMapper.selectList(sysMenuLambdaQueryWrapper);
        }else {
            //1.2 如果不是管理员 根据userId查询操作的菜单列表
            //多表关联查询：用户角色关系表，角色菜单关系表，菜单表
            //编写sql语句进行实现
            sysMenuList =  baseMapper.findMenuListByUser(userId);
        }


        //2.把查询出来的数据构建成,框架需要的路由结构
        //2.1 使用菜单操作工具类构建成树形结构
        List<SysMenu> sysMenusTree = MenuHelper.buildTree(sysMenuList);
        //2.2 构建成框架需要路由结构， this指的就是  SysMenuService
        List<RouterVo> routerVoList = this.buildRouter(sysMenusTree);
        return routerVoList;
    }


    // 构建成框架要求的路由结构  this指的就是SysMenuService
    private List<RouterVo> buildRouter(List<SysMenu> sysMenusTree) {
        //创建List集合 存储最终数据
        List<RouterVo> routerVoList = new ArrayList<>();
        //遍历sysMenusTree 树形结构但是并不 完整 放入一些必要的数据
        //主菜单  比如：系统管理 日志管理 审批设置

        sysMenusTree.stream().forEach(sysMenu -> {
            RouterVo router = new RouterVo();
            router.setHidden(false);
            router.setAlwaysShow(false);
            //使用一个自定义方法  getRouterPath(sysMenu)
            router.setPath(getRouterPath(sysMenu));
            router.setComponent(sysMenu.getComponent());
            //一个新的metaVo实现类
            router.setMeta(new MetaVo(sysMenu.getName(),sysMenu.getIcon()));

            //下一层数据部分
            List<SysMenu> children = sysMenu.getChildren();
            //分配权限 type是2，但是也存在 path component 隐藏路径
            //type=1 说明是二级菜单，在系统管理下面，比如用户管理 角色管理，菜单管理
            if (sysMenu.getType().intValue()==1){
                //首先加载出隐藏的路由
                //type = 2 component不为空
                List<SysMenu> hiddenMenuList  = children.stream()
                        .filter(item -> !StringUtils.isNullOrEmpty(item.getComponent()))
                        .collect(Collectors.toList());

                //隐藏路由可能不止一个 需要继续遍历
                //隐藏路由放入一些必要的数据 setHidden(true)
                hiddenMenuList.stream().forEach( item ->{
                    RouterVo router2 = new RouterVo();
                    //Hidden是true 说明是隐藏路由
                    router2.setHidden(true);
                    router2.setAlwaysShow(false);
                    router2.setPath(getRouterPath(sysMenu));
                    router2.setComponent(sysMenu.getComponent());
                    router2.setMeta(new MetaVo(sysMenu.getName(),sysMenu.getIcon()));
                    routerVoList.add(router2);
                });
                //else说明不是二级菜单 应该为一级菜单
            } else {
                //children不为空
                //集合不为空  说明一级菜单
                if (!CollectionUtils.isEmpty(children)){
                    if (children.size()>0){
                        //总是会被显示 (路由)
                        router.setAlwaysShow(true);
                    }
                    //进行递归操作
                    router.setChildren(buildRouter(children));
                }
            }
            routerVoList.add(router);
        });
        return routerVoList;
    }


    /**
     * 获取路由地址
     * @param menu 菜单信息
     * @return 路由地址
     */
    public String getRouterPath(SysMenu menu) {
        // 主路由要加 "/"
        String routerPath = "/" + menu.getPath();
        // 是子路由 不用加“/”
        if(menu.getParentId().intValue() != 0) {
            routerPath = menu.getPath();
        }
        return routerPath;
    }

    //根据用户ID获取用户可以操作的按钮 权限列表 perms 是permissions的简写
    @Override
    public List<String> findUserPermsByUserId(Long userId) {
        //创建List集合 存储最终数据
        List<SysMenu> sysMenuList = new ArrayList<>();
        //1. 判断当前用户是否是管理员 userId=1
        if (userId.longValue()==1){
            //1.1如果是管理员 查询出所有按钮列表
            //查询所有按钮列表
            LambdaQueryWrapper<SysMenu> lqw = new LambdaQueryWrapper<>();

            //status=1
            lqw.eq(SysMenu::getStatus,1);
            sysMenuList = baseMapper.selectList(lqw);
        }else {
            //1.2 如果不是管理员 根据userId查询可以操作的按钮 列表
            //多表关联查询： 用户角色关系表  角色菜单关系表  菜单表
            sysMenuList = baseMapper.findMenuListByUser(userId);
        }

        //2.从查询出来的数据里面, 获取可以操作的按钮List集合(perms)
        List<String> permsList = sysMenuList.stream()
                .filter(item -> item.getType() == 2)
                .map(item -> item.getPerms())
                .collect(Collectors.toList());

        return permsList;
    }
}
